package com.motorola.dp.jenkinsmonitor.service.util.dom;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.motorola.dp.jenkinsmonitor.model.util.dom.DomChildren;
import com.motorola.dp.jenkinsmonitor.model.util.dom.DomEntity;
import com.motorola.dp.jenkinsmonitor.model.util.exception.ConversionException;

@SuppressWarnings({ "rawtypes", "unchecked" })
@Component("domChildrenProcessor")
public class DomChildrenProcessor implements DomAnnotationProcessor {

    @Value("#{domAnnotationProcessorHelper}")
    private DomAnnotationProcessorHelper helper;

    @Value("#{domConversionService}")
    private DomConversionService conversionService;

    public void processField(Field field, Element node, Object model) throws Exception {
        String childrenXpath = extractChildrenXpathParam(field);
        NodeList children = retrieveNodeChildren(node, childrenXpath);

        ParameterizedType pType = (ParameterizedType) field.getGenericType();
        Class childClass = (Class) pType.getActualTypeArguments()[0];

        List<Object> childrenObjects;
        if ((DomEntity) childClass.getAnnotation(DomEntity.class) != null) {
            childrenObjects = parseNodeList(children, childClass);
        } else if (String.class == childClass) {
            childrenObjects = parseTextNodeList(children);
        } else {
            throw new ConversionException("Child entity has no mapping");
        }

        helper.applyFieldValue(model, field, childrenObjects);
    }

    private String extractChildrenXpathParam(Field field) {
        DomChildren domChildrenAnnotation = field.getAnnotation(DomChildren.class);
        String childrenXpath = domChildrenAnnotation.xpath();
        return childrenXpath;
    }

    private NodeList retrieveNodeChildren(Element parentNode, String childrenXpath) throws Exception {
        boolean xpathSpecified = !DomChildren.XPATH_UNSPECIFIED.equals(childrenXpath);
        if (xpathSpecified) {
            return helper.findNodesByXpath(parentNode, childrenXpath);
        } else {
            return parentNode.getChildNodes();
        }
    }

    private List<Object> parseNodeList(NodeList nodes, Class nodeClass) throws Exception {
        String nodeName = helper.getDomEntityName(nodeClass);
        List<Object> parsedObjects = new ArrayList<Object>();

        for (int i = 0; i < nodes.getLength(); i++) {
            Node child = nodes.item(i);

            if (nodeName.equalsIgnoreCase(child.getLocalName())) {
                Object parsedObject = conversionService.convert((Element) child, nodeClass);
                parsedObjects.add(parsedObject);
            }
        }
        return parsedObjects;
    }

    private List<Object> parseTextNodeList(NodeList nodes) {
        List<Object> parsedObjects = new ArrayList<Object>();

        for (int i = 0; i < nodes.getLength(); i++) {
            String nodeValue = nodes.item(i).getNodeValue();
            parsedObjects.add(nodeValue);
        }
        return parsedObjects;
    }

    public void setHelper(DomAnnotationProcessorHelper helper) {
        this.helper = helper;
    }

    public void setConversionService(DomConversionService conversionService) {
        this.conversionService = conversionService;
    }
}
